﻿using System;
using System.Threading;
namespace TwinHead.Frameworks
{
    /// <summary>
    /// 여러 쓰레드에서 읽기/쓰기에 대한 잠금을 제공하는 Wrapper 클래스 입니다.
    /// <remarks>
    /// 흔히 sh-lock이 ex-lock보다는 수행성능이 좋다고 평가되지만, 
    /// 항상 그런것은 아니고 lock되는 블럭에 지연이 발생할때 비로서, lock보다 좋은 비용효과를 보인다.
    /// </remarks>
    /// </summary>
    /// <example>
    /// <code>
    /// ReaderWriterObjectLocker lck = new ReaderWriterObjectLocker();
    /// ..
    /// ..
    /// using( lck.LockForRead())
    /// {
    ///     some access and read code here ...
    /// }
    /// 
    /// 
    /// ...
    /// using( lck.LockForWrite())
    /// {
    ///     some access and write code here ..
    /// }
    /// </code>
    /// </example>
    public sealed class ReaderWriterObjectLocker : IDisposable
    {
        #region BaseReleaser
        class BaseReleaser
        {
            protected ReaderWriterObjectLocker locker;

            public BaseReleaser( ReaderWriterObjectLocker locker )
            {
                this.locker = locker;
            }
        }
        #endregion

        #region ReaderReleaser
        class ReaderReleaser : BaseReleaser, IDisposable
        {
            public ReaderReleaser( ReaderWriterObjectLocker locker )
                : base( locker )
            {
            }

            #region IDisposable Members

            public void Dispose()
            {
                if (locker != null && locker.mlocker != null)
                {
                    locker.mlocker.ExitReadLock();
                }
                GC.SuppressFinalize( this );
            }

            #endregion
        }
        #endregion

        #region WriterReleaser
        class WriterReleaser : BaseReleaser, IDisposable
        {
            public WriterReleaser( ReaderWriterObjectLocker locker )
                : base( locker )
            {
            }

            #region IDisposable Members

            public void Dispose()
            {
                if (locker != null && locker.mlocker != null)
                {
                    locker.mlocker.ExitWriteLock();
                }
                GC.SuppressFinalize( this );
            }

            #endregion
        }
        #endregion

        #region Fields
        private ReaderWriterLockSlim mlocker;
        private IDisposable writerReleaser;
        private IDisposable readerReleaser;
        #endregion

        #region Constructor
        /// <summary>
        /// 기본생성자 입니다.
        /// </summary>
        public ReaderWriterObjectLocker()
        {
            mlocker = new ReaderWriterLockSlim();

            writerReleaser = new WriterReleaser( this );
            readerReleaser = new ReaderReleaser( this );
        }
        #endregion

        #region Methods
        /// <summary>
        /// 읽기 처리를 위한 잠금을 생성합니다.
        /// </summary>
        /// <returns></returns>
        public IDisposable LockForRead()
        {
            mlocker.EnterReadLock();
            return readerReleaser;
        }

        /// <summary>
        /// 쓰기 처리를 위한 잠금을 생성합니다.
        /// </summary>
        /// <returns></returns>
        public IDisposable LockForWrite()
        {
            mlocker.EnterWriteLock();
            return writerReleaser;
        }
        #endregion

        #region IDisposable 멤버

        /// <summary>
        /// 자원을 해제합니다
        /// </summary>
        public void Dispose()
        {
            if (writerReleaser != null) writerReleaser.Dispose();
            if (readerReleaser != null) readerReleaser.Dispose();

            if (mlocker != null)
            {
                mlocker.Dispose();
                mlocker = null;
            }
        }

        #endregion
    }
}
